/*
 * Copyright (C) 2000-2005 Philips Electronics N.V.
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or 
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; see the file gnu_license.txt.  If not, write to
 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  
 * 
 */
#include "semantics.h"
#include "expressions.h"
#include "errors.h"

static struct s_cfront_TraverseTable semantic_checks;

static void* check_constant_initializer(Id_Decl me, void *result)
{  
  if (declarator_init_expr (me) && 
      has_static_storage (me) &&
      !expr_is_constant_initializer(declarator_init_expr(me))) {    
    initializer_not_constant(me);   
    return (void*)FALSE;
  }
  return result;
}

static Bool elem_is_not_empty(expression e)
{ return e != NULL; }

static Bool enum_is_not_empty(declarator d)
{ return d != NULL; }

static void filter_initializers(CompoundInit e)
{
  if ( !e || !CompoundInit_inits(e)) {
    missing_initializer (e);
    return;    
  }    
  List_expression l = CompoundInit_inits(e);  
  for (; l; l = TAIL(l))
    if ( !HEAD(l) && TAIL(l) )  /* empty element, but not last */
      empty_element_in_initializer(e);  
  
  FRONT_FILTER(CompoundInit_inits(e), elem_is_not_empty);
  if ( !CompoundInit_inits(e) ) {
    missing_initializer (e);
    return;    
  }
}

static void filter_enumerators ( Enum e )
{  
#if 0
  /* LA: disabled: can be just a tag declaration */
  if ( !e || !Enum_enums(e) ) {
#else
  if ( !e ) {
#endif
    empty_enumerator_list(e);
    return;
  } 
  Bool empty = !Enum_enums(e);
  List_declarator l = Enum_enums(e);        
  for (; l; l = TAIL(l))
    if ( !HEAD(l) && TAIL(l) )  /* empty element, but not last */
      empty_element_in_enumerator(e);
  
  FRONT_FILTER(Enum_enums(e), enum_is_not_empty);
  if ( !empty && !Enum_enums(e) ) {
    missing_initializer (e);
    return;    
  }
}

Bool check_semantics(cfront cf)
{
  semantic_checks.traversal = Traverse_Down;
  semantic_checks.action_Id_Decl [PRE_ACTION] = (void*)check_constant_initializer; 
  return (Bool)Traverse_cfront(cf, &semantic_checks, (void*)TRUE);
}

void ast_filter_initializers (void *node,
                              void  (*trav_void) (void *me, Trav_void_table table))
{
  static struct s_void_cfront_TraverseTable filter_actions;
  filter_actions.action_CompoundInit[PRE_ACTION] = filter_initializers;
  filter_actions.action_Enum[PRE_ACTION] = filter_enumerators;
  trav_void(node, &filter_actions);
}
